﻿#region License

// // -------------------------------------------------------------------------------------
// // This information refers to LogOneDayPrecisionService.cs
// // 
// // Copyright 2012 ELEN7046 Group 2
// // 
// // Last Modified on: 2012-06-10
// // 
// // Contributors:
// // 
// // Amith Sewnarain
// // Elmar Vosloo
// // Jason Thorne
// // Satish Jewan
// // 
// // Wits University 
// // School of Electrical & Information Engineering
// // Software Engineering Tools and Techniques
// // 
// // -------------------------------------------------------------------------------------
// // 
// // This file (LogOneDayPrecisionService.cs) is part of the DishDash Event Visualization System.
// // 
// // DishDash is free software: you can redistribute it and/or modify
// // it under the terms of the GNU General Public License as published by
// // the Free Software Foundation, either version 3 of the License, or
// // (at your option) any later version.
// // 
// // DishDash is distributed in the hope that it will be useful,
// // but WITHOUT ANY WARRANTY; without even the implied warranty of
// // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// // GNU General Public License for more details.
// // 
// // You should have received a copy of the GNU General Public License
// // along with DishDash.  
// // If not, see <http://www.gnu.org/licenses/>.
// // -------------------------------------------------------------------------------------

#endregion

#region Using Directives

using System;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using DishDash.Common;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.MessagePatterns;

#endregion

namespace LogOneDayPrecisionApp
{
    public class LogOneDayPrecisionService : ServiceBase
    {
        private Thread workThread = null;

        protected override void OnStart(string[] args)
        {
            try
            {
                base.OnStart(args);

                // Create and start the worker thread to log any events on the queue
                workThread = new Thread(StartLogging);
                workThread.Start();
            }
            catch (Exception e)
            {
                var message = string.Format(
                    "An Exception occured while starting the service: {0}",
                    e.ToString());
                System.Diagnostics.EventLog.WriteEntry("Dish-Dash One Day Precision Logger",
                                                       message,
                                                       System.Diagnostics.EventLogEntryType.Error);
                throw;
            }
        }

        /// <summary>
        /// Starts the logging.
        /// </summary>
        private void StartLogging()
        {
            try
            {
                // Create the configuration factory
                var connectionFactory = new ConnectionFactory();
                connectionFactory.HostName = Configuration.Instance.Host;
                connectionFactory.UserName = Configuration.Instance.User;

                if (!string.IsNullOrWhiteSpace(Configuration.Instance.Password))
                {
                    connectionFactory.Password = Configuration.Instance.Password;
                }

                // Create a connection from the factory
                using (IConnection connection =
                    connectionFactory.CreateConnection())
                {
                    // Define the model, define the exchange and binding the queue required for this handler
                    using (IModel model = connection.CreateModel())
                    {
                        model.ExchangeDeclare(Configuration.Instance.ExchangeName,
                                              ExchangeType.Fanout,
                                              true);
                        var queue = model.QueueDeclare(Configuration.Instance.QueueName,
                                                       true,
                                                       false,
                                                       false,
                                                       new Dictionary<string, object>());

                        model.QueueBind(Configuration.Instance.QueueName,
                                        Configuration.Instance.ExchangeName,
                                        string.Empty,
                                        new Dictionary<string, object>());

                        // Now that the model is defined, create a subscription so that we may receive messages
                        var subscription = new Subscription(model,
                                                            Configuration.Instance.QueueName,
                                                            false);

                        while (true)
                        {
                            try
                            {
                                // Get the next message on the queue, this call is a blocking call
                                BasicDeliverEventArgs basicDeliveryEventArgs =
                                    subscription.Next();

                                // Convert the message content to a string
                                string messageContent =
                                    Encoding.UTF8.GetString(basicDeliveryEventArgs.Body);

                                // Deserialize the string into a PerformanceEvent
                                // Call the handler that will log all the events into the database

                                try
                                {
                                    // Temporary until we figure out the poison message strategy
                                    var perfEvent = PerformanceEvent.FromString(messageContent);
                                    var handler = new LogOneDayPrecision.LogOneDayPrecisionHandler();
                                    handler.Log(perfEvent);
                                }
                                catch (Exception)
                                {
                                }


                                // Acknowledge the subscription so that it is removed from the queue
                                subscription.Ack(basicDeliveryEventArgs);
                            }
                            catch (Exception exception) // Ignore the exception and try to process the next message
                            {
                                var message = string.Format(
                                    "An Exception occured while processing a message: {0}",
                                    exception.ToString());
                                System.Diagnostics.EventLog.WriteEntry(
                                    "Dish-Dash One Day Precision Logger",
                                    message,
                                    System.Diagnostics.EventLogEntryType.Error);
                            }
                        }
                    }
                }
            }
            catch (Exception e)
            {
                var message = string.Format(
                    "An Exception occured while starting the service: {0}",
                    e.ToString());
                System.Diagnostics.EventLog.WriteEntry("Dish-Dash One Day Precision Logger",
                                                       message,
                                                       System.Diagnostics.EventLogEntryType.Error);
                throw;
            }
        }

        /// <summary>
        /// When implemented in a derived class, executes when a Stop command is sent to the service by the Service Control Manager (SCM). Specifies actions to take when a service stops running.
        /// </summary>
        protected override void OnStop()
        {
            base.OnStop();

            // Stop the work thread.
            workThread.Abort();
        }
    }
}